package demo.rao.mideng.core;

import demo.rao.mideng.annotation.RaoMiDeng;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;

/**
 * <? 这是预防超时重试的幂等，所以作用针对的是【controller】
 * 借鉴com.alibaba.druid.support.spring.stat.annotation.StatAnnotationBeanPostProcessor
 * <p>
 * di相关的bean-post-processor都是实现了PriorityOrdered与MiDengBeanPostProcessor
 * <p>
 * MiDengBeanPostProcessor不可以在di相关的BeanPostProcessor之前内部的getBean，否则di就会无效。
 * <p>
 * 实现MergedBeanDefinitionPostProcessor经过二次加入，就可以强制放在bean-post-processors后面
 * 目的就是是放在 AnnotationAwareAspectJAutoProxyCreator 的后面
 * <p>
 * 创建代理对象 AbstractAutoProxyCreator.postProcessAfterInitialization 的过程--事务注解，pointcut
 */
@Slf4j
public class MiDengBeanPostProcessor /*extends AbstractAdvisingBeanPostProcessor*/ implements MergedBeanDefinitionPostProcessor /*implements EnvironmentAware*/ {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Value("${rao.mideng.before:false}")
    private boolean before;
    @Value(("${rao.mideng.expire-time:30}"))
    private String expireTime;
    private MiDengAnnoAdvisor advisor;

    /**
     * BeanPostProcessor这里是责任链模式。所以MiDengBeanPostProcessor在AbstractAdvisingBeanPostProcessor类后面就行
     * 不进行而外的处理，直接return bean就ok了
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        boolean hasRaoMiDengAnno = false;
        /**<? 如果AbstractAutoProxyCreator创建代理对象*/
        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            //拿到被代理对象的class对象
            Class<?> targetClass = advised.getTargetClass();
            //找有没有@RaoMiDeng，方法上类上是否有这个注解。
            hasRaoMiDengAnno = classHasRaoMiDengAnno(targetClass);
            if (!hasRaoMiDengAnno) {
                return bean;
            }
            if (this.advisor == null) {
                this.advisor = new MiDengAnnoAdvisor(new MiDengInterceptor(stringRedisTemplate, before, expireTime));
            }
            //继续给这个代理对象，添加代理逻辑
            advised.addAdvisor(advisor);
            return bean;
        }

        /**<? 不是aop，但是有@RaoMiDeng*/
        if (hasRaoMiDengAnno) {
            //借鉴AbstractAutoProxyCreator.postProcessAfterInitialization的createProxy
            ProxyFactory proxyFactory = new ProxyFactory();
            ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
            if (this.advisor == null) {
                this.advisor = new MiDengAnnoAdvisor(new MiDengInterceptor(stringRedisTemplate, before, expireTime));
            }
            proxyFactory.addAdvisors(advisor);
            proxyFactory.setTarget(new SingletonTargetSource(bean));
            return proxyFactory.getProxy(classLoader);
        }
        return bean;
    }

    /**
     * 递归式寻找类上有没有@RaoMiDeng注解。
     */
    private boolean classHasRaoMiDengAnno(Class<?> clazz) {
        //先检索类上是否有注解
        RaoMiDeng annotation = clazz.getAnnotation(RaoMiDeng.class);
        if (annotation != null) {
            return true;
        }
        //再去检索方法，本类上的方法
        if (methodHasRaoMiDengAnno(clazz)) {
            return true;
        } else {
            //探索父类
            Class<?> cls;
            if ((cls = clazz.getSuperclass()) != Object.class) {
                return classHasRaoMiDengAnno(cls);
            } else {
                return false;
            }
        }
    }

    /**
     * clazz.getMethods()获取的是父类的public与子类的public方法，controller的方法含有@RequstMapping都是public
     */
    private boolean methodHasRaoMiDengAnno(Class<?> clazz) {
        /**<? public方法即可*/
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (null != method.getAnnotation(RaoMiDeng.class)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {

    }
}
